// ==UserScript== // @name Enhanced Button Info Display // @namespace http://tampermonkey.net/ // @version 1.4 // @description Show button and custom component info in a draggable floating window with enhanced naming accuracy and mouse position coordinates. // @author You // @match *://*/* // @grant none // ==/UserScript== (function() { 'use strict'; let lastClickInfo = { elementOuterHTML: '???', customComponentHTML: '???', elementType: '???', elementClassName: '???', elementTitle: '???', elementID: '???', isLatest: false, customComponentName: '???' }; let hoverInfo = { elementClassName: '???', elementTitle: '???', isHovering: false, customComponentName: '???', mouseX: 0, mouseY: 0 }; window.addEventListener('load', function() { const floatingWindow = document.createElement('div'); floatingWindow.id = 'button-info-display'; floatingWindow.style.cssText = ` position: fixed; top: 10%; left: 10%; width: 80%; height: 80%; background: #ADD8E6; border: 1px solid #000; padding: 10px; overflow: auto; z-index: 10000; display: none; /* 初始时隐藏悬浮窗 */ `; document.body.appendChild(floatingWindow); const titleBar = document.createElement('div'); titleBar.style.cssText = ` background: #000; color: #fff; padding: 5px; cursor: move; `; titleBar.textContent = 'Button Info Logger'; floatingWindow.appendChild(titleBar); let isDragging = false; let offsetX, offsetY; titleBar.addEventListener('mousedown', (e) => { isDragging = true; offsetX = e.clientX - floatingWindow.getBoundingClientRect().left; offsetY = e.clientY - floatingWindow.getBoundingClientRect().top; }); document.addEventListener('mousemove', (e) => { if (isDragging) { floatingWindow.style.left = (e.clientX - offsetX) + 'px'; floatingWindow.style.top = (e.clientY - offsetY) + 'px'; } // 更新鼠标坐标 hoverInfo.mouseX = e.clientX; hoverInfo.mouseY = e.clientY; updateFloatingWindow(); }); document.addEventListener('mouseup', () => { isDragging = false; }); const toggleButton = document.createElement('button'); toggleButton.textContent = 'Toggle Button Info'; toggleButton.style.cssText = ` position: fixed; bottom: 20px; right: 20px; z-index: 10001; `; toggleButton.addEventListener('click', () => { floatingWindow.style.display = (floatingWindow.style.display === 'none')? 'block' : 'none'; }); document.body.appendChild(toggleButton); // 鼠标悬停时更新信息 document.body.addEventListener('mouseover', (event) => { const target = event.target; if (target) { updateHoverInfo(target); hoverInfo.isHovering = true; updateFloatingWindow(); } }); // 鼠标离开时清除悬停信息 document.body.addEventListener('mouseout', (event) => { const target = event.target; if (target) { hoverInfo.elementClassName = '???'; hoverInfo.elementTitle = '???'; hoverInfo.customComponentName = '???'; hoverInfo.isHovering = false; updateFloatingWindow(); } }); // 处理点击事件 document.body.addEventListener('click', (event) => { const target = event.target; if (target) { lastClickInfo.elementOuterHTML = target.outerHTML || '???'; lastClickInfo.elementClassName = target.getAttribute('class') || '???'; lastClickInfo.elementTitle = target.getAttribute('title') || '???'; lastClickInfo.elementID = target.id || '???'; lastClickInfo.elementType = target.tagName + (target.className? '.' + target.className.split(' ').join('.') : '???'); // 分析类名并确定自定义组件类型 analyzeButtonClassName(target); lastClickInfo.isLatest = true; // 标记为最新信息 updateFloatingWindow(); } }); // 分析类名并确定按钮类型 function analyzeButtonClassName(target) { const className = target.getAttribute('class'); const type = target.getAttribute('type'); const ariaLabel = target.getAttribute('aria-label'); const title = target.getAttribute('title'); if (className) { const classList = className.split(' '); if (classList.includes('button')) { lastClickInfo.customComponentName = '基础按钮'; } else if (classList.includes('btn')) { lastClickInfo.customComponentName = '框架通用按钮(如 Bootstrap 中的)'; } else if (classList.includes('ui button')) { lastClickInfo.customComponentName = 'Semantic UI 中的按钮'; } else if (classList.includes('button is-primary')) { lastClickInfo.customComponentName = 'Bulma 中的主要按钮'; } else if (classList.includes('active')) { lastClickInfo.customComponentName = '处于激活状态的按钮'; } else if (classList.includes('focus')) { lastClickInfo.customComponentName = '获得焦点的按钮'; } else if (classList.includes('hover')) { lastClickInfo.customComponentName = '鼠标悬停的按钮'; } else if (classList.includes('visited')) { lastClickInfo.customComponentName = '已访问过的按钮'; } else if (classList.includes('small')) { lastClickInfo.customComponentName = '小尺寸按钮'; } else if (classList.includes('medium')) { lastClickInfo.customComponentName = '中尺寸按钮'; } else if (classList.includes('large')) { lastClickInfo.customComponentName = '大尺寸按钮'; } else if (classList.includes('btn-sm')) { lastClickInfo.customComponentName = 'Bootstrap 中的小号按钮'; } else if (classList.includes('btn-lg')) { lastClickInfo.customComponentName = 'Bootstrap 中的大号按钮'; } else if (classList.includes('outline')) { lastClickInfo.customComponentName = '边框样式按钮'; } else if (classList.includes('rounded')) { lastClickInfo.customComponentName = '圆角按钮'; } else if (classList.includes('circle')) { lastClickInfo.customComponentName = '圆形按钮'; } else if (classList.includes('block')) { lastClickInfo.customComponentName = '占据整个父容器宽度的按钮'; } else if (classList.includes('ghost')) { lastClickInfo.customComponentName = '半透明或无背景色的按钮'; } else if (classList.includes('fade-in')) { lastClickInfo.customComponentName = '具有淡入动画效果的按钮'; } else if (classList.includes('slide-up')) { lastClickInfo.customComponentName = '具有向上滑动动画效果的按钮'; } else if (classList.includes('animated')) { lastClickInfo.customComponentName = '结合动画库的动画按钮'; } else { for (const cls of classList) { if (cls.startsWith('custom-') || cls.startsWith('my-') || cls.startsWith('action-')) { lastClickInfo.customComponentName = '自定义按钮'; break; } } if (lastClickInfo.customComponentName === '???') { lastClickInfo.customComponentName = '未知类型的按钮'; } } } else if (type ==='submit') { lastClickInfo.customComponentName = '用于提交表单的按钮'; } else if (type ==='reset') { lastClickInfo.customComponentName = '用于重置表单的按钮'; } else if (type === 'button') { lastClickInfo.customComponentName = '自定义功能的按钮'; } else if (ariaLabel || title) { lastClickInfo.customComponentName = '具有辅助信息的按钮'; } else { lastClickInfo.customComponentName = '未知类型的按钮'; } } // 更新悬浮窗 function updateFloatingWindow() { requestAnimationFrame(() => { floatingWindow.innerHTML = ''; // 清空内容 const infoHeader = document.createElement('h4'); infoHeader.textContent = 'Last Click Info:'; floatingWindow.appendChild(infoHeader); const details = ` 上次触发点击事件时的元素对象:${lastClickInfo.elementOuterHTML} 上次鼠标点击时所触发的自定义组件:${lastClickInfo.customComponentHTML} 上次鼠标点击时的元素对象/自定义组件的名称:${lastClickInfo.elementType} (${lastClickInfo.customComponentName}) 上次鼠标点击时的元素对象/自定义组件的相关代码:${lastClickInfo.elementOuterHTML} 上次鼠标点击时的元素对象/自定义组件的类名:${lastClickInfo.elementClassName} 上次鼠标点击时的元素对象/自定义组件的标题:${lastClickInfo.elementTitle} 上次鼠标点击时的元素对象/自定义组件的ID:${lastClickInfo.elementID} 上次鼠标点击信息是否完成显示:${lastClickInfo.isLatest? 'YES' : 'NO'} `; const infoElement = document.createElement('pre'); infoElement.textContent = details; floatingWindow.appendChild(infoElement); // 显示光标悬停信息 const hoverInfoHeader = document.createElement('h4'); hoverInfoHeader.textContent = 'Hover Info:'; floatingWindow.appendChild(hoverInfoHeader); const hoverDetails = ` 当前悬停按钮的类名:${hoverInfo.elementClassName} 当前悬停按钮的标题:${hoverInfo.elementTitle} 当前悬停组件名称:${hoverInfo.customComponentName} 鼠标悬停所在按钮信息:${hoverInfo.isHovering? 'YES' : 'NO'} 鼠标位置:(${hoverInfo.mouseX}, ${hoverInfo.mouseY}) // 显示鼠标位置 `; const hoverElement = document.createElement('pre'); hoverElement.textContent = hoverDetails; floatingWindow.appendChild(hoverElement); // 状态与进度条 const statusDetails = ` 上次鼠标点击时信息:${lastClickInfo.isLatest? 'YES' : 'NO'} 鼠标悬停所在按钮信息:${hoverInfo.isHovering? 'YES' : 'NO'} `; const statusElement = document.createElement('pre'); statusElement.textContent = statusDetails; floatingWindow.appendChild(statusElement); // 进度信息 let clickProgress = lastClickInfo.isLatest? '100%' : '50%'; const hoverProgress = hoverInfo.isHovering? '100%' : '未悬停在可触发点击事件的区域'; const progressDetails = ` 上次点击信息:${clickProgress} 鼠标悬停信息:${hoverProgress} `; const progressElement = document.createElement('pre'); progressElement.textContent = progressDetails; floatingWindow.appendChild(progressElement); }); } const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.tagName === 'BUTTON' || node.getAttribute('role') === 'button') { node.addEventListener('click', function () { lastClickInfo.elementOuterHTML = this.outerHTML || '???'; lastClickInfo.elementClassName = this.getAttribute('class') || '???'; lastClickInfo.elementTitle = this.getAttribute('title') || '???'; lastClickInfo.elementID = this.id || '???'; lastClickInfo.elementType = this.tagName + (this.className? '.' + this.className.split(' ').join('.') : '???'); // 分析类名并确定自定义组件信息 analyzeButtonClassName(this); lastClickInfo.isLatest = true; updateFloatingWindow(); }); } else if (node.tagName === 'G' && node.classList.contains('Z')) { lastClickInfo.elementOuterHTML = node.outerHTML || '???'; lastClickInfo.elementClassName = node.getAttribute('class') || '???'; lastClickInfo.elementTitle = node.getAttribute('title') || '???'; lastClickInfo.elementID = node.id || '???'; lastClickInfo.elementType = node.tagName + (node.className? '.' + node.className.split(' ').join('.') : '???'); // 分析类名并确定自定义组件信息 analyzeButtonClassName(node); lastClickInfo.isLatest = true; updateFloatingWindow(); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); }); })();